unit ExtendedFileTransferClientDownloadThread;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  uROClient, uROClientIntf, uRORemoteService, uROBinMessage,
  uROIndyTCPChannel, uROIndyHTTPChannel,
  ExtendedFileTransferLibrary_Intf, uROTypes;

type

  { TROThread }
  TDownloadThread = class(TThread)
  private
    fROMessage: TROBinMessage;
    fROChannel: TROIndyHTTPChannel;
    fRORemoteService: TRORemoteService;
    fOnAbort: TNotifyEvent;
    fOnFinished: TNotifyEvent;
    fOnProgress: TNotifyEvent;
    fOnStartDownload: TNotifyEvent;
    fOnError: TNotifyEvent;
    fDownloadOK: Boolean;
    fCurrentBytePos: Int64;
    fTimeStarted: TDateTime;
    fDownloadDir: string;
    fFileService: IExtendedFileTransferService;
    fErrortext: string;
    fFileName: string;
    fFileSize: Int64;
    fInfoStr: string;
    fMaxConnectionErrors, fChannelErrorscount: Integer;

    procedure RunDownload;
    procedure ROIndyChannelFailure(Sender: TROTransportChannel;
      anException: Exception; var Retry: Boolean);
    procedure Run;

  protected
  public
    property DownloadOK: Boolean read fDownloadOK;
    property FileName: string read fFileName;
    property FileSize: Int64 read fFileSize;
    property InfoStr: string read fInfoStr;
    property ErrorText: string read fErrortext;
    property CurrentBytePos: Int64 read fCurrentBytePos;
    property TimeStarted: TDateTime read fTimeStarted;
    constructor Create(aDownloadDir, aFilename: string; aFileSize: Int64;
      aOnStartDownload, aOnProgress, aOnFinished, aOnAbort, aOnError:
      TNotifyEvent);
    destructor Destroy; override;

    procedure Execute; override;
  end;

implementation
uses ExtendedFileTransferClientMain;

{ TROThread }

constructor TDownloadThread.Create(aDownloadDir, aFilename: string; aFileSize:
  Int64; aOnStartDownload, aOnProgress, aOnFinished, aOnAbort, aOnError:
  TNotifyEvent);
begin
  inherited Create(TRUE);
  fDownloadDir := aDownloadDir;
  fFilename := aFilename;
  fFileSize := aFileSize;
  fmaxconnectionerrors := 5; //try 5 times on channel-error

  fROMessage := TROBinMessage.Create(nil);

  fROChannel := TROIndyHTTPChannel.Create(nil);
  fROChannel.OnFailure := ROIndyChannelFailure;
  fROChannel.TargetURL := ExtendedFileTransferClientMainForm.ROChannel.TargetURL;
  fRORemoteService := TRORemoteService.Create(nil);

  fRORemoteService.Channel := fROChannel;
  fRORemoteService.Message := fROMessage;
  fRORemoteService.ServiceName := 'ExtendedFileTransferService';
  fFileService := fRORemoteService as IExtendedFileTransferService;

  if assigned(aOnAbort) then fOnAbort := aOnAbort;
  if assigned(aOnFinished) then fOnFinished := aOnFinished;
  if assigned(aOnProgress) then fOnProgress := aOnProgress;
  if assigned(aOnStartDownload) then fOnStartDownload := aOnStartDownload;
  if assigned(aOnError) then fOnError := aOnError;
  FreeOnTerminate:=True;
  Resume;
end;

destructor TDownloadThread.Destroy;
begin
  fFileService := nil;
  FreeAndNil(fROMessage);
  FreeAndNil(fROChannel);
  FreeAndNil(fRORemoteService);
  inherited;
end;

procedure TDownloadThread.Execute;
begin
  Run;
end;

procedure TDownloadThread.Run;
begin
  try
    RunDownload;
  finally
    if assigned(fOnFinished) then fOnFinished(Self);
  end;
end;

procedure TDownloadThread.RunDownload;
var
  NewFile: TMemoryStream;
  Chunk: Binary;
  Sequence: Integer;
begin
  fDownloadOK := false;
  Chunk := nil;
  try
    if Terminated then begin
      if assigned(fOnAbort) then fOnAbort(Self);
      exit;
    end;

    NewFile := TMemoryStream.Create;
    try
      fCurrentBytePos := 0;
      Sequence := 1;
      fTimeStarted := Now;

      fFileService.downloadsequence(fFilename, Sequence, chunk, fFileSize);

      fInfoStr := DateTimetoStr(fTimeStarted) + ' ' +
        ExtractFileName(fFilename) + ' ' +
        FloatToStrF(fFilesize / 1024, fffixed, 15, 1) + ' KB';

      if assigned(fOnStartDownload) then
        fOnStartDownload(Self);
        while (chunk <> nil) and  (Chunk.Size > 0) do begin
          if Terminated then begin
            FreeAndNil(Chunk);
            if assigned(fOnAbort) then fOnAbort(Self);
            exit;
          end;

          NewFile.Seek(0, soFromEnd);
          NewFile.CopyFrom(Chunk, Chunk.Size);
          Inc(fCurrentBytePos, Chunk.Size);

          FreeAndNil(Chunk);

          if assigned(fOnProgress) then
            fOnProgress(Self);
          Inc(Sequence);
          fFileService.downloadsequence(fFilename, Sequence, chunk, fFileSize);
        end;

      fDownloadOK := (NewFile.Size = 0) or (fFileSize = CurrentBytePos);

      if fDownloadOK then begin
        NewFile.SaveToFile(IncludeTrailingPathDelimiter(fDownloadDir) + fFilename);
        fDownloadOK := Fileexists(IncludeTrailingPathDelimiter(fDownloadDir) + fFilename);
      end;

    finally
      FreeAndNil(Chunk);
      NewFile.Free;
    end;

  except
    on e: Exception do begin
      fErrorText := e.Message;
      fDownloadOK := false;
      if assigned(fOnError) then fOnError(Self);
    end;
  end;
end;

procedure TDownloadThread.ROIndyChannelFailure(Sender: TROTransportChannel;
  anException: Exception; var Retry: Boolean);
begin
  if fChannelErrorscount > fMaxConnectionerrors then begin
    fErrorText := anException.Message;
    Self.terminate;
  end
  else begin
    inc(fChannelErrorscount);
    sleep(1000);
    Retry := true;
  end;
end;

end.

